package org.metasketch.editor.toolbox.advanced;

import groovy.lang.Binding;
import groovy.lang.GroovyShell;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.metasketch.model.BaseModel;
import org.metasketch.model.CommonIdentified;
import org.metasketch.model.IModelChangeListener;
import org.metasketch.reflection.RefUtil;
import org.metasketch.reflection.ReflectionAnalyser;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;

public class ModelViewer extends Group implements EventHandler<MouseEvent>,
		IModelChangeListener, IDragSource {

	TreeItem<String> root = new TreeItem<String>("Root node");
	TreeView treeView;

	EventHandler<ActionEvent> popupMenuHandler = new EventHandler<ActionEvent>() {

		@Override
		public void handle(ActionEvent event) {
			Object source = event.getSource();
			if (source != null && source instanceof MenuItem
					&& selection != null) {

				contextMenu.hide();
				contextMenu = null;

				ModelHandler.removeFromModelForSure(selection, model);

				selection = null;
				treeView.getSelectionModel().clearSelection();
			}

		}

	};

	RefUtil util = new RefUtil();
	private BaseModel model;

	boolean dragging = false;
	CommonIdentified selection;

	IDropHandler dropHandler;
	private ContextMenu contextMenu;
	protected ModelViewerScripts modelViewerScripts;

	public ModelViewer() {
		treeView = new TreeView();
		treeView.setShowRoot(false);
		treeView.setRoot(root);
		root.setExpanded(true);

		getChildren().add(treeView);

		treeView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
		treeView.getSelectionModel().selectedItemProperty()
				.addListener(new ChangeListener() {
					@Override
					public void changed(ObservableValue observable,
							Object oldValue, Object newValue) {
						if (newValue == null) {
							selection = null;
							return;
						} else {
							CustomTreeItem item = (CustomTreeItem) newValue;
							selection = item.getSemantic();
						}
					}
				});

		treeView.setOnMouseDragged(this);
		treeView.setOnMouseReleased(this);
		treeView.setOnMouseClicked(this);
	}

	public void setModel(BaseModel model) throws Throwable {
		model.getModelChangeManager().registerListener(this);
		this.model = model;

		refreshView();
	}

	private void refreshView() {
		root.getChildren().clear();

		List<CustomTreeItem> items = new ArrayList<CustomTreeItem>();

		Collection<CommonIdentified> nodes = model.getMainNodes();
		for (CommonIdentified node : nodes) {

			// root.
			// item.addEventHandler(EventType<MouseEvent>, this);
			items.add(addMainNode(node));
		}

		root.getChildren().addAll(items);
		root.setExpanded(true);
	}

	private CustomTreeItem addMainNode(CommonIdentified node) {
		CustomTreeItem item = new CustomTreeItem(model, node,
				model.getModelChangeManager(), modelViewerScripts, true);

		// model.getModelChangeManager().registerListener(node, item);

		return item;
	}

	@Override
	public void handle(MouseEvent event) {
		Object source = event.getSource();

		System.out.println("Model Viewer: " + event);

		if (event.getEventType() == MouseEvent.MOUSE_DRAGGED) {

			dragging = true;
			setCursor(Cursor.CLOSED_HAND);
			// event.consume();
		} else if (event.getEventType() == MouseEvent.MOUSE_RELEASED) {
			// dragging = false;
			setCursor(Cursor.DEFAULT);
			// dropHandler.handleDrop(selection);
		} else if (event.getEventType() == MouseEvent.MOUSE_CLICKED
				&& event.getButton() == MouseButton.SECONDARY
				&& selection != null) {
			closeDelete();
			// run script here...to make sure it can be deleted or not...
			// if (!(source instanceof CustomTreeItem)) {
			// event.consume();
			// return;
			// }

			try {
				// CustomTreeItem item = (CustomTreeItem) source;
				// CommonIdentified semantic = item.getSemantic();

				String script = modelViewerScripts.getDeleteTests().get(
						selection);
				if (script == null)
					script = modelViewerScripts.getDeleteTests().getInherited(
							selection);

				if (script != null) {
					Binding binding = new Binding();
					binding.setVariable("semantic", selection);
					GroovyShell shell = new GroovyShell(binding);

					Boolean delete = (Boolean) shell.evaluate(script);
					if (!delete) {
						event.consume();
						return;
					}
				}

			} catch (Throwable e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			System.err.println(source);

			contextMenu = new ContextMenu();
			contextMenu.setAutoHide(true);

			MenuItem item1 = new MenuItem("Delete from Model");
			// item1.setUserData(entry);
			item1.setOnAction(popupMenuHandler);
			contextMenu.getItems().add(item1);

			contextMenu.show((Node) source, event.getScreenX(),
					event.getScreenY());

			event.consume();
		}

	}

	protected void closeDelete() {
		if (contextMenu != null) {
			contextMenu.hide();
			contextMenu = null;
		}

	}

	// @Override
	// public void handle(ActionEvent event) {
	//
	// }

	@Override
	public void modelChanged(CommonIdentified semantic) {
		// TODO Auto-generated method stub
		// refreshView();
		// if (model.getIndex().getById(semantic.getId()) != null) {
		//
		// }

	}

	@Override
	public void relatedModelChanges() {
		// TODO Auto-generated method stub
		// refreshView();
	}

	@Override
	public void addedMainNode(CommonIdentified semantic) {
		root.getChildren().add(addMainNode(semantic));

	}

	@Override
	public void removedMainNode(CommonIdentified semantic) {
		// TODO Auto-generated method stub

	}

	@Override
	public CommonIdentified getSemantic() {

		return selection;
	}

	@Override
	public boolean isDragging() {
		return dragging;
	}

	@Override
	public void stopDragging() {
		dragging = false;

	}

	@Override
	public void setDropHandler(IDropHandler dropHandler) {
		this.dropHandler = dropHandler;

	}

	public void setScripts(ModelViewerScripts modelViewerScripts) {
		this.modelViewerScripts = modelViewerScripts;

	}

}
